package cs.uns.edu.ar.aygps.service.data;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;

import cs.uns.edu.ar.aygps.model.exception.AyGPSException;

public class XMLHelper {
	
	public static class DTDEntityResolver implements EntityResolver, Serializable {
		private static final String PATH = "aygps.dtd";
		private transient ClassLoader resourceLoader;

		/**
		 * Default constructor using DTDEntityResolver classloader for resource
		 * loading.
		 */
		public DTDEntityResolver() {
			// backward compatibility
			resourceLoader = Thread.currentThread().getContextClassLoader();
		}

		/**
		 * Set the class loader used to load resouces
		 * 
		 * @param resourceLoader
		 *          class loader to use
		 */
		public DTDEntityResolver(ClassLoader aResourceLoader) {
			resourceLoader = aResourceLoader;
		}

		public InputSource resolveEntity(String publicId, String systemId) {
			InputStream dtdStream = resourceLoader == null ? getClass().getResourceAsStream(PATH) : resourceLoader.getResourceAsStream(PATH);
			if (dtdStream == null)
				return null;

			InputSource source = new InputSource(dtdStream);
			source.setPublicId(publicId);
			source.setSystemId(PATH);
			return source;
		}
	}
	
	public static class ErrorLogger implements ErrorHandler {
		private List errors;

		ErrorLogger(List errorList) {
			this.errors = errorList;
		}

		public void error(SAXParseException error) {
			errors.add(error);
		}

		public void fatalError(SAXParseException error) {
			error(error);
		}

		public void warning(SAXParseException warn) {}
	}

	
	
	
	private static final EntityResolver DEFAULT_DTD_RESOLVER = new DTDEntityResolver();
	
	
	private static InputStream getResource(String fileName) throws AyGPSException {
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		InputStream rsrc = null;
		if (contextClassLoader != null) {
			rsrc = contextClassLoader.getResourceAsStream(fileName);
		}

		if (rsrc == null)
			throw new AyGPSException("Resource: " + fileName + " not found");

		return rsrc;
	}
	
	public Document parseStream(String path) throws AyGPSException {
		
		List errors = new ArrayList();
		
		InputStream xmlInputStream = getResource(path);

		try {
			Document doc = createSAXReader(errors).read(new InputSource(xmlInputStream));
			if (errors.size() != 0) {
				throw new AyGPSException("invalid mapping");
			}

			return doc;
		} catch (DocumentException e) {
			e.printStackTrace();
			throw new AyGPSException(AyGPSException.ERROR_PARSING_CONFIG_FILE);
		} finally {
			try {
				xmlInputStream.close();
			} catch (IOException ioe) {}
		}
	}
	
	private static SAXReader createSAXReader(List errorsList) {
		SAXReader saxReader = new SAXReader();
		saxReader.setErrorHandler(new ErrorLogger(errorsList));
		saxReader.setMergeAdjacentText(true);
		saxReader.setValidation(true);
		saxReader.setEntityResolver(DEFAULT_DTD_RESOLVER);

		return saxReader;
	}
}
